/*
 * @(#)EnhancedPreferences.java 0.1 12/28/04
 *
 * Copyright 2004 Fabio Gasparetti. All rights reserved.
 */

package org.tabularium.util;

import java.util.Properties;
import java.lang.String;
import java.lang.Integer;
import java.util.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;

import java.io.*;

/**
 * A <code>Properties</code> (semi-)wrapper that provides some extra
 * funcionality, like:
 * <li> <code>getPropertyAsXXX</code> (where <codeXXX</code> could be Boolean,
 * DateTime, Int);
 * <li> multi-value properties.
 *
 * @author Fabio Gasparetti
 * @version 0.1
 */
public class EnhancedPreferences {
    /* It is not implemented as Decorator because it hasn't the abstract base
     * class; so it was indispensable to redefine all the Properties's methods.
     * It could be an inherited class, but I want that the end-user has the
     * possibility to still use the Properties class.
     */
    protected Properties properties;
    protected String separator = new String(",");

    /**
     * @since 1.0
     */
    public EnhancedPreferences() {
        properties = new Properties();
    }

    public EnhancedPreferences(Properties props) {
        properties = props;
    }

    /**
     * @param key
     * @return
     */
    public void setValueSeparator(String sep) {
        separator = sep;
    }

    /**
     * Simple <code>Properties</code>'s <code>getProperty</code> wrapper.
     * Usefull just to provide uniform interface.
     * @param key
     * @return
     */
    public String getProperty(String key) {
        return properties.getProperty(key);
    }

    /**
     * Simple <code>Properties</code>'s <code>getProperty</code> wrapper.
     * Usefull just to provide uniform interface.
     * @param key
     * @param defaultValue
     * @return
     */
    public String getProperty(String key, String defaultValue) {
        return properties.getProperty(key, defaultValue);
    }

    /**
     * Simple <code>Properties</code>'s <code>setProperty</code> wrapper.
     * Usefull just to provide uniform interface.
     * @param key
     * @param defaultValue
     * @return
     */
    public Object setProperty(String key, String value) {
        return properties.setProperty(key, value);
    }

    public int getPropertyAsInt(String key)
            throws NumberFormatException {
        String p = properties.getProperty(key, "-1");
        return (new Integer(p)).intValue();
    }

    /**
     * Like <code>getProperyAsInt(String)</code> but, if parse error happen,
     * <code>defaultValue</code> is returned.
     *
     * @param key
     * @param defaultValue
     * @return
     */
    public int getPropertyAsInt(String key, int defaultValue) {
        int ret = defaultValue;
        try {
            String p = properties.getProperty(key);
            if (p != null)
                ret = (new Integer(p)).intValue();
        }
        catch (Exception ex) { }
        return ret;
    }

    public float getPropertyAsFloat(String key)
            throws NumberFormatException {
        String p = properties.getProperty(key, "-1f");
        return (new Float(p)).floatValue();
    }

    /**
     * Like <code>getProperyAsInt(String)</code> but, if parse error happen,
     * <code>defaultValue</code> is returned.
     *
     * @param key
     * @param defaultValue
     * @return
     */
    public float getPropertyAsFloat(String key, float defaultValue) {
        float ret = defaultValue;
        try {
            String p = properties.getProperty(key);
            if (p != null)
                ret = (new Float(p)).floatValue();
        }
        catch (Exception ex) { }
        return ret;
    }

    /**
     *
     * @param key
     * @return
     * @throws NumberFormatException
     */
    public boolean getPropertyAsBoolean(String key)
            throws NumberFormatException {
        boolean b = false;
        String p = properties.getProperty(key, "false");
        if (p != null) {
            b = p.equalsIgnoreCase("true") || p.equalsIgnoreCase("yes");
        }
        return b;
    }

    public boolean getPropertyAsBoolean(String key, boolean defaultValue) {
        boolean ret = defaultValue;
        try {
            ret = getPropertyAsBoolean(key);
        }
        catch (Exception ex) {
        }
        return ret;
    }

    public Date getPropertyAsDateTime(String key, int dateStyle, int timeStyle)
            throws NumberFormatException {
        String p = properties.getProperty(key);
        if (p == null) {
            return null;
        }
        Date d;
        try {
            // DateFormat.LONG, DateFormat.FULL
            DateFormat df;
            if (dateStyle != -1) {
                if (timeStyle != -1)
                    df = DateFormat.getDateTimeInstance(dateStyle, timeStyle);
                else
                    df = DateFormat.getDateInstance(dateStyle);
            }
            else if (timeStyle != -1)
                df = DateFormat.getTimeInstance(timeStyle);
            else
                return null;

            df.setLenient(true);
            d = df.parse(p);
        }
        catch (Exception ex) {
            return null;
        }
        return d;
    }

    public Date getPropertyAsDateTime(String key, String pattern)
            throws NumberFormatException {
        String p = properties.getProperty(key);
        if (p == null) {
            return null;
        }
        Date d;
        try {
            // DateFormat.LONG, DateFormat.FULL
            DateFormat df =  new SimpleDateFormat(pattern);
            //df.setLenient(true);
            d = df.parse(p);
        }
        catch (Exception ex) {
            return null;
        }
        return d;
    }

    /**
     * Like <code>getProperyAsDate(String)</code> but, if parse error happen,
     * <code>defaultValue</code> is returned.
     *
     * @param key
     * @param defaultValue
     * @return
     */
    public Date getPropertyAsDateTime(String key, int dateStyle,
                                      int timeStyle, Date defaultValue) {
        Date ret = defaultValue;
        try {
            ret = getPropertyAsDateTime(key, dateStyle, timeStyle);
        }
        catch (Exception ex) { }
        return ret;
    }

    public Object setProperty(String key, boolean value) {
        if (value) {
            return properties.setProperty(key, "true");
        }
        return properties.setProperty(key, "false");
    }

    /**
     * @param key
     * @return
     */
    public Set getPropertyValues(String key) {
        String s = getProperty(key, "");
        Set s1 = EnhancedPreferences.splitValue(s, separator);
        return s1;
    }

    public Object setProperty(String key, Date date,
                              int dateStyle, int timeStyle) {
        String s = new String();
        DateFormat df;
        if (dateStyle != 0) {
            if (timeStyle != 0) {
                df = DateFormat.getDateTimeInstance(dateStyle, timeStyle);
            }
            else {
                df = DateFormat.getDateInstance(dateStyle);
            }
        }
        else if (timeStyle != 0) {
            df = DateFormat.getTimeInstance(timeStyle);
        }
        else {
            return null;
        }

        s = df.format(date);
        return properties.setProperty(key, s);
    }

    public Object setProperty(String key, int value) {
        return properties.setProperty(key, (new Integer(value)).toString());
    }

    public Object clone() {
        EnhancedPreferences pe = new EnhancedPreferences();
        pe.properties = (Properties)properties.clone();
        return pe;
    }

    public boolean equals(Object ojb) {
        /**@todo equals() implementation */
        return false;
    }

    public boolean propertyContains(String key, String value) {
        String s = getProperty(key);
        return EnhancedPreferences.splitValue(s, separator).contains(value);
    }

    public boolean addProperty(String key, String value) {
/*
        String s = getProperty(key, "");
        Iterator iter1, iter2;
        Collection c = EnhancedPreferences.splitValue(s, separator);
        iter1 = EnhancedPreferences.splitValue(value, separator).iterator();
        while (iter1.hasNext()) {
            String value1 = (String)iter1.next();
            if (!c.contains(value1)) {
                if (s.length() > 0) {
                    s = s + separator + value1;
                }
                else {
                    s = value1;
                }
            }
            iter2 = .iterator();
            while (iter2.hasNext()) {
                String item = (String)iter2.next();
                if (item.compareTo(value1) == 0) {
                    break;
                }
            }
        }
        properties.setProperty(key, s);
        return true;
*/
        String s = getProperty(key, "");
        Set s1 = EnhancedPreferences.splitValue(s, separator);
        Set s2 = EnhancedPreferences.splitValue(value, separator);
        s1.addAll(s2);
        properties.setProperty(key, EnhancedPreferences.makeupValue(s1, separator));
        return true;
    }

    public boolean propertyContains(String key, Date date) {
        /**@todo propertContains implementation*/
        return false;
    }

    public boolean propertyContains(String key, int value) {
        /**@todo propertContains implementation*/
        return false;
    }

    public boolean removeProperty(String key, String value) {
        String s = getProperty(key, "");
        Set s1 = EnhancedPreferences.splitValue(s, separator);
        Set s2 = EnhancedPreferences.splitValue(value, separator);
        s1.removeAll(s2);
        properties.setProperty(key, EnhancedPreferences.makeupValue(s1, separator));
        return true;
    }

    public String toString() {
        return properties.toString();
    }

    /**
     * Split a key's value consisting of a sequence of sub-value separated
     * with a special character.
     */
    public static Set splitValue(String value, String ch) {
        HashSet v = new HashSet();
        if (value != null) {
            StringTokenizer st = new StringTokenizer(value, ch);
            while (st.hasMoreTokens()) {
               v.add(st.nextToken().trim());
            }
        }
        return v;
    }

    public static String makeupValue(Set values, String sep) {
        StringBuffer s = new StringBuffer();
        Iterator iter1 = values.iterator();
        while (iter1.hasNext()) {
            String value1 = (String)iter1.next();
            if (s.length() > 0) {
                s.append(sep);
            }
            s.append(value1);
        }
        return s.toString();
    }

    public static void main(String[] args) {
        Properties properties = new Properties();
        try {
            FileInputStream in = new FileInputStream("pippo.properties");
            properties.load(in);
            in.close();
        }
        catch (Exception ex) {
//            logger.log(Level.WARNING, "Exception occours opening properties file:'"+propsFilename+"', try to run with default settings.");
        }
//        Properties = new
        EnhancedPreferences pei = new EnhancedPreferences(properties);
//        pei.loadProperties();
//        System.out.println(pei.toString());
//        pei.setProperty("key1", new Date());
        pei.setProperty("key3", "pippo");
        pei.addProperty("key2", "123");
        pei.addProperty("key3", "pippo");
        pei.addProperty("key3", "pippo, pluto, paperino,");
//        System.out.println(pei.getPropertyAsDate("key1"));
        System.out.println(pei.getPropertyAsInt("key2"));
        System.out.println(pei.propertyContains("key3", "pluto"));
        System.out.println(pei.propertyContains("key3", "paperino"));
        System.out.println(pei.propertyContains("key3", "pippo"));
        System.out.println(pei.propertyContains("key3", "xxx"));
//        pei.saveProperties();
        System.out.println(pei.toString());
    }

}
